/*
 * LogDatabase.c
 *      Author: dephille
 */


#include "LogDatabase.h"



/**
 * Checks if a table exists with the given name.
 *
 * @param	tableName Name of the table to look for.
 * @returns True if the table exists, false otherwise or on error.
 */
static gboolean	PC_LogDB_TableExists(const gchar* tableName) {
	sqlite3_stmt*	stmt;	// Clean-up: Yes

	// Check input:
	if (!tableName) {
		purple_debug_warning("PidginCote", "PC_LogDB_TableExists: Missing 'tableName' parameter.\r\n");
		return FALSE;
	}

	// Check if there's an active DB connection:
	if (!pc_logDB) {
		purple_debug_error("PidginCote", "PC_LogDB_TableExists: No active DB connection found.\r\n");
		return FALSE;
	}

	// Make statement:
	stmt = NULL;
	if (sqlite3_prepare_v2(pc_logDB, "SELECT name FROM sqlite_master WHERE name=? LIMIT 1", -1, &stmt, NULL) != SQLITE_OK) {
		purple_debug_error("PidginCote", "PC_LogDB_TableExists: Unable to prepare statement due to SQLite error.\r\n");
		return FALSE;
	} else if (!stmt) {
		purple_debug_error("PidginCote", "PC_LogDB_TableExists: Unable to prepare statement due to unknown error.\r\n");
		return FALSE;
	}

	// Bind variables:
	if (sqlite3_bind_text(stmt, 1, tableName, -1, SQLITE_STATIC) != SQLITE_OK) {
		purple_debug_error("PidginCote", "PC_LogDB_TableExists: Unable to bind first value.\r\n");
		return FALSE;
	}

	// Fetch one row:
	if (sqlite3_step(stmt) == SQLITE_ROW) {
		// Table exists:
		sqlite3_finalize(stmt);
		return TRUE;
	} else {
		// Table does not exist:
		sqlite3_finalize(stmt);
		return FALSE;
	}
}



/**
 * Executes a query on the database.
 *
 * @param	query	The query to execute.
 * @returns True on success, false on failure.
 */
static gboolean PC_LogDB_Query(const gchar* query) {
	char*	errMsg;	// Clean-up: Yes

	// Check input:
	if (!query) {
		purple_debug_warning("PidginCote", "PC_LogDB_Query: Missing 'query' parameter.\r\n");
		return FALSE;
	}

	// Check database:
	if (!pc_logDB) {
		purple_debug_error("PidginCote", "PC_LogDB_Query: No active database connection.\r\n");
		return FALSE;
	}

	// Execute query:
	if (sqlite3_exec(pc_logDB, query, NULL, 0, &errMsg) != SQLITE_OK) {
		purple_debug_error("PidginCote", "PC_LogDB_Query: Query failed.\r\n\tQuery: %s\r\n\tError: %s\r\n", query, errMsg);
		sqlite3_free(errMsg);
		return FALSE;
	} else {
		return TRUE;
	}
}



/**
 * Gets the table name for an account.
 *
 * @param	protocol	The protocol of the account
 * @param	account		The username
 * @returns The table name for the given account or NULL if failed.
 */
static gchar* PC_LogDB_GetTableName(const gchar* protocol, const gchar* account) {
	gchar*				tableName;	// Clean-up: No
	sqlite3_stmt*		stmt;		// Clean-up: Yes
	pc_dbtable_account*	accountRow;	// Clean-up: Yes

	// Check input:
	if (!protocol) {
		purple_debug_warning("PidginCote", "PC_LogDB_GetTableName: Missing 'protocol' parameter.\r\n");
		return NULL;
	} else if (!account) {
		purple_debug_warning("PidginCote", "PC_LogDB_GetTableName: Missing 'account' parameter.\r\n");
		return NULL;
	} else if (!pc_logDB) {
		purple_debug_warning("PidginCote", "PC_LogDB_GetTableName: No active database connection found.\r\n");
		return NULL;
	}

	// Make statement:
	stmt = NULL;
	if (sqlite3_prepare_v2(pc_logDB, PC_LOGDB_QUERY_GETTABLENAME, -1, &stmt, NULL) != SQLITE_OK) {
		purple_debug_error("PidginCote", "PC_LogDB_GetTableName: Unable to prepare statement due to SQLite error.\r\n");
		return NULL;
	} else if (!stmt) {
		purple_debug_error("PidginCote", "PC_LogDB_GetTableName: Unable to prepare statement due to unknown error.\r\n");
		return NULL;
	}

	// Bind variables:
	if (sqlite3_bind_text(stmt, 1, protocol, -1, SQLITE_STATIC) != SQLITE_OK) {
		purple_debug_error("PidginCote", "PC_LogDB_GetTableName: Unable to bind first value.\r\n");
		return NULL;
	}
	if (sqlite3_bind_text(stmt, 2, account, -1, SQLITE_STATIC) != SQLITE_OK) {
		purple_debug_error("PidginCote", "PC_LogDB_GetTableName: Unable to bind second value.\r\n");
		return NULL;
	}

	// Fetch one row:
	accountRow = PC_DBTable_Accounts_FetchOne(stmt);
	if (!accountRow) {
		// Make account entry:
		accountRow = PC_DBTable_Accounts_New();
		if (!accountRow) {
			purple_debug_error("PidginCote", "PC_LogDB_GetTableName: Unable to allocate Account Entry object.\r\n");
			return NULL;
		}

		// Fill info:
		accountRow->account = g_strdup(account);
		accountRow->protocol = g_strdup(protocol);

		// Insert:
		if (!PC_DBTable_Accounts_Insert(accountRow)) {
			purple_debug_error("PidginCote", "PC_LogDB_GetTableName: Unable to insert new Account entry.\r\n");
			return NULL;
		}

		// Clean-up:
		PC_DBTable_Accounts_Delete(accountRow);
		accountRow = NULL;

		// Retry query:
		sqlite3_reset(stmt);
		accountRow = PC_DBTable_Accounts_FetchOne(stmt);
		if (!accountRow) {
			purple_debug_error("PidginCote", "PC_LogDB_GetTableName: Unable to find newly inserted Account entry.\r\n");
			return NULL;
		}
	}

	// Generate table name:
	tableName = g_strdup_printf("Logs_%i", accountRow->id);

	// Clean-up:
	sqlite3_finalize(stmt);
	PC_DBTable_Accounts_Delete(accountRow);

	// Return name:
	return tableName;
}



/**
 * Initializes the Log database.
 */
static void	PC_LogDB_Init(void) {
	// Open a database connection:
	PC_LogDB_Open();
}



/**
 * Uninitializes the Log database.
 */
static void	PC_LogDB_Uninit(void) {
	// Close any open database:
	PC_LogDB_Close();
}



/**
 * Opens or creates the database.
 */
static void PC_LogDB_Open(void) {
	gchar*			dbLocation;	// Clean-up: Yes
	const gchar*	query;		// Clean-up: No

	// Only open the database if it is not already open:
	if (pc_logDB) {
		purple_debug_warning("PidginCote", "PC_LogDatabase_Open: Database already open.\r\n");
		return;
	}

	// Open database:
	dbLocation = g_strdup_printf("%s/logs/pc_logDB.db", purple_user_dir());
	if (sqlite3_open(dbLocation, &pc_logDB) != SQLITE_OK) {
		purple_debug_error("PidginCote", "PC_LogDatabase_Open: Unable to make/open database: %s\r\n", dbLocation);
		g_free(dbLocation);
		pc_logDB = NULL;
		return;
	}

	// Clean-up:
	g_free(dbLocation);

	// Create Accounts table if necessary:
	if (!PC_LogDB_TableExists("Accounts")) {
		query = "CREATE TABLE IF NOT EXISTS Accounts ("
				"'id' INTEGER PRIMARY KEY AUTOINCREMENT,"
				"'protocol' VARCHAR(25) NOT NULL,"
				"'account' VARCHAR(100) NOT NULL"
				");"
				"CREATE INDEX Accounts_protocol ON Accounts (protocol);"
				"CREATE INDEX Accounts_account ON Accounts (account);";
		if (!PC_LogDB_Query(query)) {
			purple_debug_error("PidginCote", "PC_LogDB_Open: Unable to create Accounts table.\r\n");
			pc_logDB = NULL;
		}
	}
}



/**
 * Closes the existing database connection.
 */
static void PC_LogDB_Close(void) {
	// Only close the database if it is opened:
	if (pc_logDB) {
		sqlite3_close(pc_logDB);
		purple_debug_info("PidginCote", "PC_LogDatabase_Close: Database closed.\r\n");
	} else {
		purple_debug_warning("PidginCote", "PC_LogDatabase_Close: Database was already closed.\r\n");
	}
}
